package org.simpleframework.core;

import org.simpleframework.core.annotation.Component;
import org.simpleframework.core.annotation.Controller;
import org.simpleframework.core.annotation.Repository;
import org.simpleframework.core.annotation.Service;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;


import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 容器：采用枚举的方式创建
 *
 */
public class BeanContainer {

    private BeanContainer(){}

    private enum ContainerHolder{
        HOLDER;
        private BeanContainer beanContainer;
        ContainerHolder(){
            beanContainer = new BeanContainer();
        }
    }

    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.beanContainer;
    }


    // 保存Class对象、及其实例的载体
    private final Map<Class<?>,Object> beanMap = new ConcurrentHashMap();

    // 注解配置载体(从容器中选出被这些注解配置标记的Class对象,存入到beanMap载体)
    private final static List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);

    //
    public synchronized void loadBeans(String packageName) {
        // 判断bean容器是否被加载过
        if(isLoaded()){
            // log.warn(BeanContainer has been loaded.)
            return;
        }
        // 获取package下的所有Class对象
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if(ValidationUtil.isEmpty(classSet)){
            //log.warm(“nothing extract from packageName")
            return;
        }
        // 检查每个Class对象是否有注解标签
        for(Class<?> clazz:classSet){
            for(Class<? extends Annotation> annotaion:BEAN_ANNOTATION){
                // 将目标类作为key,实例作为value，放入到beanMap中
                if(clazz.isAnnotationPresent(annotaion)){
                    beanMap.put(clazz,ClassUtil.newInstance(clazz));
                }
            }
        }
        loaded = true;
    }

    /**
     * 添加一个Class对象，及其Bean实例
     * @param clazz
     * @param bean
     * @return
     */
    public Object addBean(Class<?> clazz, Object bean){
        return beanMap.put(clazz,bean);
    }

    /**
     * 返回被删除Bean的实例
     * @param clazz
     * @return
     */
    public Object removeBean(Class<?> clazz){
        return beanMap.remove(clazz);
    }

    /**
     * 根据Class对象，返回Bean实例
     * @param clazz
     * @return
     */
    public Object getBean(Class<?> clazz){
        return beanMap.get(clazz);
    }

    /**
     * 返回容器载体里，所有Bean的键位
     * @return
     */
    public Set<Class<?>> getClasses(){
        return beanMap.keySet();
    }

    /**
     * 获取所有的beans
     * @return
     */
    public Set<Object> getBeans(){
        return new HashSet<>(beanMap.values());
    }

    /**
     * 根据注解配置，筛选出Bean的Class对象集合
     * @param anotation
     * @return
     */
    public Set<Class<?>> getClassesByAnnotaion(Class<? extends Annotation> anotation){
        // 获取beanMap的Class对象
        Set<Class<?>> keySet = getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            return null;
        }
        Set<Class<?>> classSet = new HashSet<>();
        // 通过注解筛选被注解标记的class对象，并添加到classSet中
        for(Class<?> clazz:keySet){
            // 类是否被相关的annotation标记
            if(clazz.isAnnotationPresent(anotation)){
                classSet.add(clazz);
            }
        }
        return classSet.size()>0?classSet:null;
    }

    /**
     * 根据注解配置，筛选出Bean的Class对象集合
     * @param interfaceOrClass
     * @return
     */
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
    // 获取beanMap的Class对象
    Set<Class<?>> keySet = getClasses();
    if(ValidationUtil.isEmpty(keySet)){
        return null;
    }
    // 判断keySet是接口或类的子类，如果是添加到ClassSet中
    Set<Class<?>> classSet = new HashSet<>();
    // 通过注解筛选被注解标记的class对象，并添加到classSet中
    for(Class<?> clazz:keySet){
        // 判断当前对象是否是参数的父类或本身
        if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
            classSet.add(clazz);
        }
    }
    return classSet.size()>0?classSet:null;
}
    private boolean loaded = false;
    public boolean isLoaded() {
        return loaded;
    }

    public void setLoaded(boolean loaded) {
        this.loaded = loaded;
    }
    /**
     * Bean实例数量
     * @return
     */
    public int size(){
        return beanMap.size();
    }
}
